package com.how2java.tmall.service;

import com.how2java.tmall.dao.ProductDao;
import com.how2java.tmall.es.ProductESDao;
import com.how2java.tmall.pojo.Category;
import com.how2java.tmall.pojo.Product;
import com.how2java.tmall.util.Page4Navigator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@CacheConfig(cacheNames = "products")
public class ProductService {

    @Autowired
    private ProductDao productDao;
    @Autowired
    private ProductESDao productESDao;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private ProductImageService productImageService;
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private ReviewService reviewService;
    @CacheEvict(allEntries = true)
    public void add(Product bean){
        productDao.save(bean);
        productESDao.save(bean);
    }
    @CacheEvict(allEntries = true)
    public void delete(int id){
        productDao.delete(id);
        productESDao.delete(id);
    }
    @CacheEvict(allEntries = true)
    public void update(Product bean){
        productDao.save(bean);
        productESDao.save(bean);
    }
    @Cacheable(key="'products-one-'+#p0")
    public Product get(int id){
        return productDao.findOne(id);
    }

    @Cacheable(key="'products-cid-'+#p0+'-page-'+#p1+'-'+#p2")
    public Page4Navigator<Product> list(int cid,int start,int size,int navigatePages){
        Category category = categoryService.get(cid);

        Sort sort = new Sort(Sort.Direction.DESC,"id");

        Pageable pageable = new PageRequest(start,size,sort);
        Page<Product> page = productDao.findByCategory(category,pageable);
        Page4Navigator<Product> p = new Page4Navigator<>(page,navigatePages);
        productImageService.setFirstProductImages(p.getContent());
        return p;
    }

    /**
     * 这个 listByCategory 方法本来就是 ProductService 的方法，却不能直接调用。 为什么呢？
     * 因为 springboot 的缓存机制是通过切面编程 aop来实现的。
     * 从fill方法里直接调用 listByCategory 方法， aop 是拦截不到的，也就不会走缓存了。
     * 所以要通过这种 绕一绕 的方式故意诱发 aop, 这样才会想我们期望的那样走redis缓存。
     * @param category
     */
    public void fill(Category category){
//        ProductService productService = SpringContextUtil.getBean(ProductService.class);
        List<Product> productList = listByCategory(category);
        productImageService.setFirstProductImages(productList);
        category.setProductList(productList);

    }

    public void fill(List<Category> categories){
        for(Category category : categories){
            fill(category);
        }
    }

    public void fillByRow(List<Category> categories){
        int productNumberEachRow = 8;
        for(Category category : categories){
            List<Product> products = category.getProductList();
            List<List<Product>> productsByRow = new ArrayList<>();
            for(int i =0 ;i<products.size();i+=productNumberEachRow){
                int size = i +productNumberEachRow;
                size = size > products.size()?products.size():size;
                List<Product> productsOfEachRow = products.subList(i,size);
                productsByRow.add(productsOfEachRow);
            }
            category.setProductsByRow(productsByRow);
        }
    }

    @Cacheable("'products-cid-'+#p0.id")
    public List<Product> listByCategory(Category category) {
        return productDao.findByCategoryOrderById(category);
    }

    public void setSaleAndReviewNumber(List<Product> products){
        for(Product product : products){
            setSaleAndReviewNumber(product);
        }
    }

    public void setSaleAndReviewNumber(Product product ){
        int saleCount = orderItemService.getSaleCount(product);
        product.setSaleCount(saleCount);
        int reviewCount = reviewService.getCount(product);
        product.setReviewCount(reviewCount);
    }

//    public List<Product> search(String keyword,int start,int size){
//        Sort sort = new Sort(Sort.Direction.DESC,"id");
//        Pageable pageable = new PageRequest(start,size,sort);
//        List<Product> products = productDao.findByNameLike("%"+keyword+"%",pageable);
//        return products;
//    }

    public List<Product> search(String keyword,int start,int size){
        initDatabase2ES();
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery()
                                                                .add(QueryBuilders.matchPhrasePrefixQuery("name",keyword),
                                                                        ScoreFunctionBuilders.weightFactorFunction(100))
                                                                .scoreMode("sum")
                                                                .setMinScore(10);
        Sort sort = new Sort(Sort.Direction.DESC,"id");
        Pageable pageable = new PageRequest(start,size,sort);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withPageable(pageable)
                .withQuery(functionScoreQueryBuilder).build();

        Page<Product> page = productESDao.search(searchQuery);
        return page.getContent();
    }

    private void initDatabase2ES(){
        Pageable pageable = new PageRequest(0,5);
        Page<Product> page = productESDao.findAll(pageable);
        if(page.getContent().isEmpty()){
            List<Product> products = productDao.findAll();
            for(Product product : products){
                productESDao.save(products);
            }
        }
    }


}
